package com.sunny.原子性.Atomic包;

import com.sunny.anotation.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

@ThreadSafe
public class AtomicIntegerTest {
    private static Logger log = LoggerFactory.getLogger(AtomicIntegerTest.class);

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行数量
    public static int threadTotal = 200;

    // 从int类型换成AtomicInteger
    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        // 创建一个可缓存线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        // Semaphore:信号量，设置同时并发执行最大限制数
        final Semaphore semaphore = new Semaphore(threadTotal);
        /* CountDownLatch是一个同步工具类，它允许一个或多个线程一直等待，直到其他线程的操作执行完后再执行
        CountDownLatch是通过一个计数器来实现的，计数器的初始值为线程的数量。每当一个线程完成了自己的任务后，
        计数器的值就会减1。当计数器值到达0时，它表示所有的线程已经完成了任务，然后在闭锁上等待的线程就可以恢复执行任务*/
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);

        // 循环执行5000次请求， 但是控制每次的最大并行数量为200，通过信号量进行控制
        for(int i = 0; i < clientTotal; ++i) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire(); // 获取许可
                        add(count);
                        semaphore.release(); // 释放许可
                    } catch (InterruptedException e) {
                        log.error("exception", e);
                    }

                    countDownLatch.countDown(); // 减到0，等待的主线程就可以工作了
                }
            });
        }

        /**
         * 主线程必须在启动其他线程后立即调用CountDownLatch.await()方法。
         * 这样主线程的操作就会在这个方法上阻塞，直到其他线程完成各自的任务
         * count的值等于0，然后主线程就能通过await()方法，恢复执行自己的任务。
         */
        countDownLatch.await();

        /**
         * 当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。
         * 此时，则不能再往线程池中添加任何任务，否则将会抛出RejectedExecutionException异常。
         * 但是，此时线程池不会立刻退出，直到添加到线程池中的任务都已经处理完成，才会退出
         */
        executorService.shutdown();

        log.info("count:{}", count.get());

    }

    /**
     * 调用原子性的自增操作
     * @param integer
     */
    private static void add(AtomicInteger integer) {
        integer.getAndIncrement();
        // integer.incrementAndGet();
    }
}
